package sundun.sharding.algorithm;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import sundun.sharding.common.AbstractComplexSharding;
import sundun.sharding.common.complex.ComplexShardingParam;
import sundun.sharding.common.complex.ComplexShardingParamValue;
import sundun.sharding.common.complex.ComplexShardingType;
import sundun.sharding.constant.ShardingConstant;
import sundun.sharding.enums.ShardingTableCacheEnum;
import sundun.sharding.properties.ShardingJdbcProperties;
import sundun.sharding.utils.ShardingAlgorithmUtil;
import sundun.sharding.utils.SpringUtil;
import sundun.sharding.utils.TimeFrameEndpointUtil;


import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 自定义分片策略
 * 先通过时间字段以年份分组，再针对年份下面设置多个节点进行存储
 * 分片格式 表名_年份_节点（ims_yesterday_jqxx_2024_0）
 * （仅支持两个字段分片，取第一个分片字段为范围分片，取第二个字段为取模分片 ）
 * @author zxl
 * @date 2024/07/31 15:03
 */
@Slf4j
public class CustomComplexShardingAlgorithm  extends AbstractComplexSharding<Long> implements ComplexKeysShardingAlgorithm<Comparable<?>> {


    @Override
    public Collection<String> doSharding(Collection<String> tableNames, ComplexKeysShardingValue<Comparable<?>> shardingValue) {
        Instant start = Instant.now();
        String logicTableName = shardingValue.getLogicTableName();
        ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
        ShardingAlgorithmUtil.createAllTable(logicTable, tableNames);
        if (logicTable == null) {
            log.error("【{}】数据表类型错误，请稍后重试，logicTableNames：{}，logicTableName:{}",
                    logicTableName,ShardingTableCacheEnum.logicTableNames(), logicTableName);
            throw new IllegalArgumentException("数据表类型错误，请稍后重试！");
        }
        //获取分片字段
        String complexColumns = ShardingAlgorithmUtil.getComplexColumns(logicTableName);
        String[] split = complexColumns.split(",");
        if (split.length < 2) {
            log.error("【{}】当前获取分片字段配置=【{}】",logicTableName,complexColumns);
            throw new IllegalArgumentException("分片字段配置数量错误，请稍后重试！");
        }
        /// 打印分片信息
        log.info("【{}】精确分片，节点配置表名：{}，数据库缓存表名：{}", logicTableName, tableNames, logicTable.resultTableNamesCache());
        // 初始化分库分表参数
        ComplexShardingType sharingParams = super.complexKeyInitSharingParams(shardingValue);
        if (CollectionUtils.isEmpty(sharingParams.getShardingParam())){
            return logicTable.resultTableNamesCache();
        }
        String rangColumn = split[0];
        String nodeColumn = split[1];
        Map<String, List<ComplexShardingParam>> collect = sharingParams.getShardingParam()
                .stream().collect(Collectors.groupingBy(ComplexShardingParam::getColumnName));
        List<ComplexShardingParam> complexShardingParams = collect.get(rangColumn);
        if (CollectionUtils.isEmpty(complexShardingParams)){
            return logicTable.resultTableNamesCache();
        }

        // 确定涉及的分片
        Collection<String> shardingResult = new LinkedHashSet<>();
        try {
            if (sharingParams.getIsAddData()) {
                complexShardingParams.stream().map(s ->{
                    //根据时间进行范围分片
                    ComplexShardingParamValue<? extends Comparable<?>> complexShardingParamValue = s.getComplexShardingParamValue();
                    Date date = StringConvertDate(String.valueOf(complexShardingParamValue.getSingleValue()));
                    Instant instant = date.toInstant();
                    LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
                    String resultTableName = logicTableName + "_" + ShardingConstant.DATE_TIME_YEAR_FORMATTER.format(localDateTime);
                    //根据节点字段取模分片
                    List<ComplexShardingParam> complexShardingParams1 = collect.get(nodeColumn);
                    //取模分片算法 确定节点数
                    nodeShardingAlgorithm(complexShardingParams1,shardingResult,resultTableName);
                    return s;
                }).collect(Collectors.toList());
                Instant end = Instant.now();
                log.info("【{}】分表查询结束耗时:{}ms",logicTableName,Duration.between(start, end).toMillis());
                return ShardingAlgorithmUtil.getShardingTablesAndCreate(logicTable,shardingResult);
            } else {
                //存在范围查询
                complexShardingParams.stream().map(s ->{
                    //根据时间进行范围分片
                    // between and 的起始值
                    ComplexShardingParamValue<? extends Comparable<?>> complexShardingParamValue1 = s.getComplexShardingParamValue();
                    boolean hasLowerBound = complexShardingParamValue1.isLowerBound();
                    boolean hasUpperBound = complexShardingParamValue1.isUpperBound();

                    // 获取最大值和最小值
                    Set<String> tableNameCache = logicTable.resultTableNamesCache();
                    String min = hasLowerBound ? String.valueOf(complexShardingParamValue1.getLowerEndPoint()) : TimeFrameEndpointUtil.getLowerEndpointYear(tableNameCache,logicTableName);
                    String max = hasUpperBound ? String.valueOf(complexShardingParamValue1.getUpperEndPoint()) : TimeFrameEndpointUtil.getUpperEndpointYear(tableNameCache,logicTableName);

                    Date minDate = null;
                    Date maxDate = null;
                    try {
                        minDate = ShardingConstant.DATE_TIME_FORMATTER_YEAR.parse(min);
                        maxDate = ShardingConstant.DATE_TIME_FORMATTER_YEAR.parse(max);
                    } catch (ParseException e) {
                        throw new RuntimeException(e);
                    }
                    Calendar calendar = Calendar.getInstance();
                    while (minDate.before(maxDate) || minDate.equals(maxDate)) {
                        String tableName = logicTableName + ShardingConstant.TABLE_SPLIT_SYMBOL + ShardingConstant.DATE_TIME_FORMATTER_YEAR.format(minDate);
                        //根据节点字段取模分片
                        List<ComplexShardingParam> complexShardingParams1 = collect.get(nodeColumn);
                        //取模分片算法 确定节点数
                        nodeShardingAlgorithm(complexShardingParams1,shardingResult,tableName);
                        // 设置Calendar的时间为Date对象的时间
                        calendar.setTime(minDate);
                        // 给日期加一年
                        calendar.add(Calendar.YEAR, 1);
                        minDate = calendar.getTime();
                    }
                    return s;
                }).collect(Collectors.toList());
                Instant end = Instant.now();
                log.info("【{}】分表查询结束耗时:{}ms",logicTableName,Duration.between(start, end).toMillis());
                return ShardingAlgorithmUtil.getShardingTablesAndCreate(logicTable,shardingResult);
            }
        } catch (Exception e) {
            log.error("分片操作异常，采用全分片查询，ex=【{}】",e);
            return logicTable.resultTableNamesCache();
        }
    }


    /**
     * 取模分片算法 确定节点数
     * @param complexShardingParams 分片字段
     * @param shardingResult 返回实际表名
     * @param tableName
     */
    private void nodeShardingAlgorithm(List<ComplexShardingParam> complexShardingParams, Collection<String> shardingResult,String  tableName){
        ShardingJdbcProperties shardingJdbcProperties = SpringUtil.getBean(ShardingJdbcProperties.class);
        if (CollectionUtils.isNotEmpty(complexShardingParams)){
            complexShardingParams.stream().map(node ->{
                //兼容当前查询参数存在in方式
                if (node.isSingleValue()) {
                    String gxdwbhstr = String.valueOf(node.getComplexShardingParamValue().getSingleValue());
                    this.deliveryAlgorithm(gxdwbhstr,tableName,shardingResult,shardingJdbcProperties);
                } else {
                    node.getComplexShardingParamValue().getListValue().stream().map(param -> {
                        String gxdwbhstr = String.valueOf(param);
                        this.deliveryAlgorithm(gxdwbhstr,tableName,shardingResult,shardingJdbcProperties);
                        return param;
                    }).collect(Collectors.toList());
                }
                return node;
            }).collect(Collectors.toList());
        } else {
            for (int i = 0; i < shardingJdbcProperties.getNodes(); i++) {
                // 构造表名
                String tableNameNode = tableName + ShardingConstant.TABLE_SPLIT_SYMBOL + i;
                shardingResult.add(tableNameNode);
            }
        }
    }



    @Override
    protected void deliveryAlgorithm(String singleValue, String tableName, Collection<String> shardingResult, ShardingJdbcProperties shardingJdbcProperties) {
        singleValue = singleValue.replaceAll("[\\[\\]]", ""); // 清除字符串中的"["和"]"
        // 根据分片键值取前六位，可自定义，防止字段过长导致Long类型转换失败
        String substring = singleValue.substring(0,6);
        int tableIndex = Math.abs(Integer.parseInt(substring) % shardingJdbcProperties.getNodes());
        // 构造表名
        String tableNameNode = tableName + ShardingConstant.TABLE_SPLIT_SYMBOL + tableIndex;
        shardingResult.add(tableNameNode);
    }

    @Override
    public void init() {

    }

    @Override
    public String getType() {
        return null;
    }


    private Date StringConvertDate(String dateStr){
        Date date = null;
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            date =dateFormat.parse(dateStr);
        } catch (ParseException  e) {
            throw new RuntimeException(e);
        }
        return date;
    }



}
